package com.lkd.plcs7.direct.s7.task;

import com.github.xingshuangs.iot.protocol.s7.service.S7PLC;
import com.lkd.plcs7.direct.config.PlcConfigProperties;
import lombok.extern.slf4j.Slf4j;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;

@Slf4j
public class Collect implements Runnable {
    // 采集间隔
    private int intervalMs;

    //缓存采集间隔
    private int intervalMsCache;


    // 采集次数（每达到这个次数计算一次平均值）
    private int averageCount;


    //缓存采集次数
    private int averageCountCache;


    /**
     * //plc 实例
     */
    private S7PLC s7PLC;

    /**
     * 读取地址
     */
    private String readAddress;

    /**
     * 写入地址
     */
    private String writeAddress;


    /**
     * 采集数据的时间间隔的plc地址
     */
    private String intervalSecondsAddress;

    /**
     * 采集次数（每达到这个次数计算一次平均值）从plc内部获取
     */
    private String averageCountAddress;// 写入地址


    private int count = 0;
    private List<Float> values = new ArrayList<>();
    private long loopCount = 1;

    private final ExecutorService executorService;

    /**
     * 构造方法
     *
     * @param s7PLC
     * @param executorService
     * @param plcConfig
     */
    public Collect(S7PLC s7PLC, ExecutorService executorService, PlcConfigProperties plcConfig) {
        this.s7PLC = s7PLC;
        this.executorService = executorService;
        this.readAddress = plcConfig.getReadAddress();
        this.writeAddress = plcConfig.getWriteAddress();
        this.intervalSecondsAddress = plcConfig.getIntervalSecondsAddress();
        this.averageCountAddress = plcConfig.getAverageCountAddress();
    }

    @Override
    public void run() {
        while (true) {
            executorService.execute(() -> {
                try {
                    float intervalSeconds = s7PLC.readFloat32(intervalSecondsAddress);
                    this.intervalMsCache = (int) (intervalSeconds * 1000);
                    float plcAverageCount = s7PLC.readFloat32(averageCountAddress);
                    this.averageCountCache = (int) plcAverageCount;
                } catch (Exception e) {
                    log.error("获取采集间隔和采集次数失败", e);
                }
            });

            if (count == 0) {
                //新的采集周期按照最新的缓存的周期与采集次数
                 this.intervalMs= this.intervalMsCache;
                 this.averageCount = this.averageCountCache;
                log.info("————————————————————[{}] 线程开始进行第{}次数据采集————————————————————", Thread.currentThread().getName(), loopCount);
                log.info("该次采集间隔为: {} 毫秒 , 采集次数为：{}",intervalMs,averageCount);
                loopCount++;
            }

            // 这里保持循环不断，持续采集
            float readFloat32 = s7PLC.readFloat32(readAddress);
            log.info("第{}采集的值为{}",count, readFloat32);
            values.add(readFloat32);
            count++;
            if (count >= averageCount) {
                float averageValue = calculateAndPrintAverage();// 达到指定次数后计算平均值
                //进行异步写入操作
                executorService.execute(() -> {
                    try {
                        float readFloat = s7PLC.readFloat32(writeAddress);
                        log.info("平均值写入后{}值为：{}", writeAddress, readFloat);
                        s7PLC.writeFloat32(writeAddress, averageValue);
                    } catch (Exception e) {
                        log.error("写入{}失败: {}", writeAddress, e.toString());
                    }
                });
                count = 0; // 重置计数器，准备下一轮采集的计数
                log.info(values.toString());
                values.clear(); // 清空值列表，为下一轮采集做准备
            }
            try {
                Thread.sleep(intervalMs); // 维持采集间隔
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt(); // 保留中断状态
                // 不主动跳出循环，希望线程能持续运行
            }
        }
    }

    private float calculateAndPrintAverage() {
        float sum = 0;
        for (Float value : values) {
            sum += value;
        }
        float average = sum / values.size();
        log.info("采集{}次的平均值为: {}", averageCount, average);
        return average;
    }

}
